<?php
declare (strict_types = 1);
namespace app\admin\controller;

use think\App;
use think\exception\ValidateException;
use think\Validate;
use \Firebase\JWT\JWT;

use app\admin\controller\PublicController;

/**
 * 控制器基础类
 */
abstract class BaseController extends PublicController
{
    /**
     * Request实例
     * @var \think\Request
     */
    protected $request;

    /**
     * 应用实例
     * @var \think\App
     */
    protected $app;

    /**
     * 是否批量验证
     * @var bool
     */
    protected $batchValidate = false;

    /**
     * 控制器中间件
     * @var array
     */
    protected $middleware = [];

    /**
     * 构造方法
     * @access public
     * @param  App  $app  应用对象
     */
    public function __construct(App $app)
    {
        $this->app     = $app;
        $this->request = $this->app->request;

        // 控制器初始化
        $this->initialize();
    }

    // 初始化
    protected function initialize()
    {
        // 保存参数
        $this->setAction();
        
        // sign验证
        // $this->checkSign();

        // 不需要 Token 验证的方法
        $ext = [
            'login/login', // 登录
            'user/addUser',
            'resume/setResumeInviteOut',
            'contact/addContactUs',
            'contact/addJoinConsult',
            'check/outCheckRecord',
            'project/downJobFile',
            'record/setMyMoneyOut',
            'product/notify_code',
            'product/notify_renew',
        ];

        if (!in_array($this->mvc, $ext)){
            // token 验证
            $check = $this->checkToken();

            if($check === false){
                $this->error_back("请登录后操作");
            }
        }
    }

    /**
     * 接收数据
     */
    private function setAction()
    {
        $this->controller = request()->controller();
        $this->action = request()->action();
        $this->mvc = strtolower($this->controller) . "/" . $this->action;
        $this->param = request()->isPost() ? input('post.') : input('get.');
        $this->param['token'] = $this->request->header('token');
    }

    /**
     * Token 校验
     * @return bool
     */
    protected function checkToken()
    {
        //获取 JWT 密钥
        $key = config('api.jwt');

        //判断 Token 是否存在
        if(!isset($this->param['token']) || $this->param['token'] == null) {
            return false;
        }

        try{
            //解密 JWT 字符串
            $param_decode = JWT::decode($this->param['token'], $key, ['HS256']);
            $param_decode = (array)$param_decode;
        }catch(\Exception $e){
            $this->error_back($e->getMessage());
        }

        //判断 user_id 是否存在
        if (!isset($param_decode['user_id']) || $param_decode['user_id'] == null) {
            return false;
        }

        //给 user_id 赋值
        $this->user_id = $param_decode['user_id'];
        return true;
    }

    /**
     * sign验证
     */
    private function checkSign()
    {
        // 取出所有参数
        $param = $this->param;

        // 判断 sign 是否存在
        if (!isset($param['sign']) || $param['sign'] == null) {
            $this->error_back('缺少sign');
        }

        // 头部加密字符串
        $str = config('api.sign');

        // 取出并删除sign
        $sign = $param['sign'];
        unset($param['sign']);

        // 数组按照键升序排序
        ksort($param);
      
        foreach ($param as $key => $value){
            // 排除二维数组
            if (is_array($value)) {
                continue;
            }

            if ($this->is_json($value) !== false) {
                continue;
            }

            // 拼接键和值
            $str.= $key. $value;
        }

        // 生成新sign
        $checkSign = md5($str);

        if ($checkSign != $sign) {
            $this->error_back('sign错误 '.$checkSign);
        }
    }

    private function  is_json($data = '', $assoc = false) 
    {
        $data = json_decode($data, $assoc);
        if (($data && is_object($data)) || (is_array($data) && !empty($data))) {
            return $data;
        }
        return false;
    }

    /**
     * 验证数据
     * @access protected
     * @param  array        $data     数据
     * @param  string|array $validate 验证器名或者验证规则数组
     * @param  array        $message  提示信息
     * @param  bool         $batch    是否批量验证
     * @return array|string|true
     * @throws ValidateException
     */
    protected function validate(array $data, $validate, array $message = [], bool $batch = false)
    {
        if (is_array($validate)) {
            $v = new Validate();
            $v->rule($validate);
        } else {
            if (strpos($validate, '.')) {
                // 支持场景
                [$validate, $scene] = explode('.', $validate);
            }
            $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
            $v     = new $class();
            if (!empty($scene)) {
                $v->scene($scene);
            }
        }

        $v->message($message);

        // 是否批量验证
        if ($batch || $this->batchValidate) {
            $v->batch(true);
        }

        return $v->failException(true)->check($data);
    }

}
